﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Cngot.Extensions.DependencyInjection
{
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddAutoDependencyInjection(this IServiceCollection services, IEnumerable<Assembly> assemblies = null)
        {
            if (assemblies == null || !assemblies.Any())
            {
                assemblies = new List<Assembly>() { Assembly.GetEntryAssembly() };
            }

            var types = assemblies.SelectMany(a => a.GetTypes());
            var serviceTypes = types.Where(t => t.IsDefined(typeof(DependencyInjectionAttribute), false)).ToList();

            foreach (var type in serviceTypes)
            {
                var attribute = type.GetCustomAttribute<DependencyInjectionAttribute>(false);

                AddService(services, attribute.ServiceType, type, attribute.Lifetime, attribute.Instance);
            }

            return services;
        }

        private static void AddService(IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime, string instance)
        {
            var methodName = lifetime switch
            {
                ServiceLifetime.Transient => nameof(ServiceCollectionServiceExtensions.AddTransient),
                ServiceLifetime.Scoped => nameof(ServiceCollectionServiceExtensions.AddScoped),
                ServiceLifetime.Singleton => nameof(ServiceCollectionServiceExtensions.AddSingleton),
                _ => throw new ArgumentOutOfRangeException()
            };

            if (string.IsNullOrEmpty(instance))
            {
                if (lifetime == ServiceLifetime.Transient)
                {
                    services.AddTransient(serviceType, implementationType);
                }
                else if (lifetime == ServiceLifetime.Scoped)
                {
                    services.AddScoped(serviceType, implementationType);
                }
                else if (lifetime == ServiceLifetime.Singleton)
                {
                    services.AddSingleton(serviceType, implementationType);
                }
            }

            else
            {
                if (lifetime == ServiceLifetime.Transient)
                {
                    services.AddKeyedTransient(serviceType, instance, implementationType);
                }
                else if (lifetime == ServiceLifetime.Scoped)
                {
                    services.AddKeyedScoped(serviceType, instance, implementationType);
                }
                else if (lifetime == ServiceLifetime.Singleton)
                {
                    services.AddKeyedSingleton(serviceType, instance, implementationType);
                }
            }
        }
    }
}
